#include <stdlib.h>
#ifdef HASHDEBUG
# include <stdio.h>
#endif
#include <string.h>
#include "4p.h"

/* hash.c */

int hash_chunks = 0;

static int hash_key;
#define getkey(p, l) ((tolower(*(uc *)s) + tolower(*((uc *)s + l / 2) << 4) ^ l) & 255)

hash_t
hash_new_(hash_t h)
{
  hash_t r = malloc(512 * sizeof(*r)); AN(r);
  memset(r, 0, 512 * sizeof(*r));
  if (h) *h = r;
  hash_chunks++;
  return r;
}

static hash_t
hash_lookup_ (hash_t h, char *s, int flag)
{
  int l = strlen(s);
  hash_key = getkey(s, l);
  for (hash_key || hash_key++; *(h + hash_key) && strcmp(s, *(h + hash_key)); h = *h)
    if (!*h)
      return flag ? hash_new_(h): NULL;
  return h;
}

void *
hash_lookup_i (hash_t h, void *s)
{
  int l = strlen(s);
  hash_key = getkey(s, l);
  for (hash_key || hash_key++; h && *(h + hash_key); h = *h)
    if (!strcasecmp(s, *(h + hash_key)))
      return *(h + hash_key + 256);
  return NULL;
}

void *
hash_lookup (hash_t h, void *s)
{
  hash_t r = hash_lookup_(h, s, 0);
  return r != NULL ? *(r + hash_key + 256) : NULL;
}

static void hash_moveup (hash_t h, hash_t t);
static void
hash_moveup (hash_t h, hash_t t) {
  hash_t hh = *h;
  if (hh != t) hash_moveup(hh, t);
  *(hh += hash_key) = *(h += hash_key);
  *(hh + 256) = *(h + 256);
#ifdef HASHDEBUG
  fprintf(stderr, ".");
#endif
  return;
}

void
hash_insert (hash_t h, void *s, void *info)
{
  hash_t hx = hash_lookup_(h, s, 1);
  if (h != hx) {
#ifdef HASHDEBUG
    fprintf(stderr, "moveup %s ", s);
#endif
    hash_moveup(h, hx);
#ifdef HASHDEBUG
    fprintf(stderr, "\n");
#endif
  }
  h += hash_key;
  *h = s;
  *(h + 256) = info;
  return;
}
